/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.syot;

import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.TextView;


/**
 * View that draws, takes keystrokes, etc. for a simple Syot game.
 * 
 * Has a mode which RUNNING, PAUSED, etc. Has a x, y, dx, dy, ... capturing the
 * current ship physics. All x/y etc. are measured with (0,0) at the lower left.
 * updatePhysics() advances the physics based on realtime. draw() renders the
 * ship, and does an invalidate() to prompt another draw() as soon as possible
 * by the system.
 */
class SyotView extends SurfaceView implements SurfaceHolder.Callback {
    class SyotThread extends Thread implements SensorEventListener {
        /*
         * Difficulty setting constants
         */
        public static final int DIFFICULTY_EASY = 0;
        public static final int DIFFICULTY_HARD = 1;
        public static final int DIFFICULTY_MEDIUM = 2;
        /*
         * Physics constants
         */
        public static final int PHYS_DOWN_ACCEL_SEC = 35;
        public static final int PHYS_FIRE_ACCEL_SEC = 80;
        public static final int PHYS_FUEL_INIT = 60;
        public static final int PHYS_FUEL_MAX = 100;
        public static final int PHYS_FUEL_SEC = 10;
        public static final int PHYS_SLEW_SEC = 120; // degrees/second rotate
        public static final int PHYS_SPEED_HYPERSPACE = 180;
        public static final int PHYS_SPEED_INIT = 30;
        public static final int PHYS_SPEED_MAX = 120;
        /*
         * State-tracking constants
         */
        public static final int STATE_LOSE = 1;
        public static final int STATE_PAUSE = 2;
        public static final int STATE_READY = 3;
        public static final int STATE_RUNNING = 4;
        public static final int STATE_WIN = 5;

        /*
         * Goal condition constants
         */
        public static final int TARGET_ANGLE = 18; // > this angle means crash
        public static final int TARGET_BOTTOM_PADDING = 17; // px below gear
        public static final int TARGET_PAD_HEIGHT = 8; // how high above ground
        public static final int TARGET_SPEED = 28; // > this speed means crash
        public static final double TARGET_WIDTH = 1.6; // width of target
        /*
         * UI constants (i.e. the speed & fuel bars)
         */
        public static final int UI_BAR = 100; // width of the bar(s)
        public static final int UI_BAR_HEIGHT = 10; // height of the bar(s)
        private static final String KEY_DIFFICULTY = "mDifficulty";
        private static final String KEY_DX = "mDX";

        private static final String KEY_DY = "mDY";

        private static final String KEY_X = "mX";
        private static final String KEY_Y = "mY";
		private static final String KEY_HEADING = "mHeading";

        /*
         * Member (state) fields
         */
        /** The drawable to use as the background of the animation canvas */
        private Bitmap mBackgroundImage;

        /**
         * Current height of the surface/canvas.
         * 
         * @see #setSurfaceSize
         */
        private int mCanvasHeight = 1;

        /**
         * Current width of the surface/canvas.
         * 
         * @see #setSurfaceSize
         */
        private int mCanvasWidth = 1;

        /**
         * Current difficulty -- amount of fuel, allowed angle, etc. Default is
         * MEDIUM.
         */
        private int mDifficulty;

        /** Velocity dx. */
        private double mDX;

        /** Velocity dy. */
        private double mDY;

        /** Message handler used by thread to interact with TextView */
        private Handler mHandler;

        /**
         * Lander heading in degrees, with 0 up, 90 right. Kept in the range
         * 0..360.
         */
        private double mHeading;

        /** Used to figure out elapsed time between frames */
        private long mLastTime;

        /** Paint to draw the lines on screen. */
        private Paint mLinePaint;

        /** "Bad" speed-too-high variant of the line color. */
        private Paint mLinePaintBad;

        /** The state of the game. One of READY, RUNNING, PAUSE, LOSE, or WIN */
        public int mMode;

        /** Currently rotating, -1 left, 0 none, 1 right. */
        private int mRotating;

        /** Indicate whether the surface has been created & is ready to draw */
        private boolean mRun = false;
    
        private int trackRun;
        private int trackRunn;
 
        private ArrayList<Rect> mRoadDivider = new ArrayList<Rect>();
        
        private int mNumberOfMarkers;
        
        private int mTrackHeight;
        private int mTrackWidth;

        private Car mCar;
        
        private float mSensorX;
        private float mSensorY;
        private long mSensorTimeStamp;
        private long mCpuTimeStamp;
        
        /** Handle to the surface manager object we interact with */
        private SurfaceHolder mSurfaceHolder;

        private SensorManager myOwnSensorManager;
        
        private Sensor mAccelerometer;
        private Sensor mOrientationmeter;

        /** Number of wins in a row. */
        private int mWinsInARow;

        /** X of lander center. */
        private double mX;

        /** Y of lander center. */
        private double mY;
        
    	private MediaPlayer mp;
    	
        private class Car {
        	
        	public static final int TURN_LEFT = -1;
        	public static final int TURN_RIGHT = 1;
        	public static final int ACCELERATE = 1;
        	public static final int BREAK = -1;
        	public static final int NONE = 0;
        	public static final int MAX_CAR_SPEED = 20;//40;	// m/s or 135 km/h
        	public static final int METER_PIXEL_RATIO = 16;	// 1 meter = 16 pixels
        	public static final int TRACK_LIMIT_LEFT = 0;
        	public static final int TRACK_LIMIT_RIGHT = 200;
        	
            /** What to draw for the Lander in its normal state */
            private Drawable mCarImage;
            
            private int carWidth;
            private int carHeight;
            
        	private double speed; 			// in m/s
        	
        	private double heading; 		// 0...360
        	private int rotation; 			// 0=nothing, 1=clockwise, -1=anticlockwise
        	private int accelerationOn;	// 1=on, 0=off, -1=break
        	
        	private Rect location;
        	
        	public void accelerate(int onoff) {
        		accelerationOn = onoff;
        	}
        	
        	public void turn(int leftOrRight) {
        		//rotation = leftOrRight;
        		heading += leftOrRight;
        	}
        	
        	public void angleDirect(float angle) {
        		heading = angle;
        	}
        	
        	public Car(Context context) {
        		
        		mCarImage = context.getResources().getDrawable(R.drawable.car);
        		speed = 0;
        		heading = 0;
        		rotation = 0;
        		carWidth = mCarImage.getIntrinsicWidth();
        		carHeight = mCarImage.getIntrinsicHeight();
        		location = new Rect(150, 250, 150 + carWidth, 250 + carHeight);
        	}
            
        	public void draw(Canvas canvas) { // draw the f****** car
        	
                canvas.save();

                canvas.rotate((float) heading,
                			  (float) (location.left + carWidth / 2),
                			  (float) (location.top + carHeight / 2));
                mCarImage.setBounds(location);
                mCarImage.draw(canvas);
                
                canvas.restore();
        	}
        	
        	public void update(double elapsed) { // update the car physics
        		
        		if (mMode == STATE_RUNNING) {
        			accelerationOn = ACCELERATE;
        			if (accelerationOn == ACCELERATE) {
        				speed += 1 + speed * elapsed;
        				if (speed > MAX_CAR_SPEED) speed = MAX_CAR_SPEED;
        				
        			} else if (accelerationOn == BREAK) {
        				speed -= speed * elapsed * 4;
        				if (speed < 0) speed = 0;
        			}
        			
//	        		heading += rotation * elapsed * PHYS_SLEW_SEC;
//	        		heading %= 360;
//	        		
//	        		if (heading < 0) {
//	        			heading += 360;
//	        		}
	        		
	        		//double distanceCovered = speed;  // in meters
	        		
	        		trackRun = (int) ((int)speed * elapsed * METER_PIXEL_RATIO);  // in meters
	        		
	        		//Log.w(this.getClass().getName(), "elapsed" + elapsed);
	        		
	        		double dx = trackRun * Math.sin(Math.PI * heading / 180);
	        		 trackRunn = (int) (trackRun * Math.cos(Math.PI * heading / 180));
	        		
	        		//Log.w(this.getClass().getName(), "trackRunn = " + trackRunn + ", heading = " + heading + ", trackRun" + trackRun);
	        		 
	        		location.set(location.left + (int)dx,
	        					 location.top,
	        					 location.left + carWidth + (int)dx, 
	        					 location.top + carHeight);
	        		
	        		if (location.left <= TRACK_LIMIT_LEFT) {
	        			location.left  = TRACK_LIMIT_LEFT;
	        			location.right = carWidth + TRACK_LIMIT_LEFT;
	        		}
	        		else if (location.right >= mTrackWidth) {
	        			location.left = mTrackWidth - carWidth;
	        			location.right = mTrackWidth;
	        		}
        		}
        	}
        } 

        public SyotThread(SurfaceHolder surfaceHolder, Context context,
                Handler handler) {
           
            // get handles to some important objects
            mSurfaceHolder = surfaceHolder;
            mHandler = handler;
            mContext = context;

            Resources res = context.getResources();
            
            // load background image as a Bitmap instead of a Drawable b/c
            // we don't need to transform it and it's faster to draw this way
            mBackgroundImage = BitmapFactory.decodeResource(res,
                    R.drawable.track);

            // Initialize paints for speedometer
            mLinePaint = new Paint();
            mLinePaint.setAntiAlias(true);
            mLinePaint.setARGB(255, 0, 255, 0);

            mLinePaintBad = new Paint();
            mLinePaintBad.setAntiAlias(true);
            mLinePaintBad.setARGB(255, 120, 180, 0);
 
            mTrackHeight = 400;
            mTrackWidth  = 240;

            mNumberOfMarkers = mTrackHeight / (20 * 2) + 1;
            
            mRoadDivider.clear();
            
            for (int i = 0; i < mNumberOfMarkers; i++) {
            	mRoadDivider.add(new Rect(0, 0, 0, 0));
            }
            
            mWinsInARow = 0;
            mDifficulty = DIFFICULTY_MEDIUM;
            
            mCar = new Car(context);
            
            mp = MediaPlayer.create(context, R.raw.engine);
            mp.setOnCompletionListener(onCompletion);
        }
        
        public void startSimulation() {
            /*
             * It is not necessary to get accelerometer events at a very high
             * rate, by using a slower rate (SENSOR_DELAY_UI), we get an
             * automatic low-pass filter, which "extracts" the gravity component
             * of the acceleration. As an added benefit, we use less power and
             * CPU resources.
             */
        	myOwnSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
        	myOwnSensorManager.registerListener(this, mOrientationmeter, SensorManager.SENSOR_DELAY_UI);
        }

        public void getSensorManager(SensorManager sensorManager) {
        	myOwnSensorManager = sensorManager;
        	
            mAccelerometer    = myOwnSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
            mOrientationmeter = myOwnSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
        }

        public void stopSimulation() {
        	myOwnSensorManager.unregisterListener(this);
        }
        
        /**
         * Starts the game, setting parameters for the current difficulty.
         */
        public void doStart() {
            synchronized (mSurfaceHolder) {

                int speedInit = PHYS_SPEED_INIT;

                // Adjust difficulty params for EASY/HARD
                if (mDifficulty == DIFFICULTY_EASY) {
                    speedInit = speedInit * 3 / 4;
                } else if (mDifficulty == DIFFICULTY_HARD) {
                    speedInit = speedInit * 4 / 3;
                }
                
                /*
                 * It is not necessary to get accelerometer events at a very high
                 * rate, by using a slower rate (SENSOR_DELAY_UI), we get an
                 * automatic low-pass filter, which "extracts" the gravity component
                 * of the acceleration. As an added benefit, we use less power and
                 * CPU resources.
                 */
                //mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);

                // pick a convenient initial location for the lander sprite
                mX = mCanvasWidth / 2;
//                mY = mCanvasHeight - mLanderHeight / 2;

                // start with a little random motion
                mDY = Math.random() * -speedInit;
                mDX = Math.random() * 2 * speedInit - speedInit;
                mHeading = 0;

                mLastTime = System.currentTimeMillis() + 100;
                setState(STATE_RUNNING);
            }
        }

        /**
         * Pauses the physics update & animation.
         */
        public void pause() {
            synchronized (mSurfaceHolder) {
                if (mMode == STATE_RUNNING) setState(STATE_PAUSE);
            }
        }

        /**
         * Restores game state from the indicated Bundle. Typically called when
         * the Activity is being restored after having been previously
         * destroyed.
         * 
         * @param savedState Bundle containing the game state
         */
        public synchronized void restoreState(Bundle savedState) {
            synchronized (mSurfaceHolder) {
                setState(STATE_PAUSE);
                mRotating = 0;

                mDifficulty = savedState.getInt(KEY_DIFFICULTY);
                mX = savedState.getDouble(KEY_X);
                mY = savedState.getDouble(KEY_Y);
                mDX = savedState.getDouble(KEY_DX);
                mDY = savedState.getDouble(KEY_DY);
                mHeading = savedState.getDouble(KEY_HEADING);
            }
        }

        @Override
        public void run() {
            while (mRun) {
                Canvas c = null;
                try {
                    c = mSurfaceHolder.lockCanvas(null);
                    synchronized (mSurfaceHolder) {
                        if (mMode == STATE_RUNNING) updatePhysics();
                        doDraw(c);
                    }
                } finally {
                    // do this in a finally so that if an exception is thrown
                    // during the above, we don't leave the Surface in an
                    // inconsistent state
                    if (c != null) {
                        mSurfaceHolder.unlockCanvasAndPost(c);
                    }
                }
            }
        }

        /**
         * Dump game state to the provided Bundle. Typically called when the
         * Activity is being suspended.
         * 
         * @return Bundle with this view's state
         */
        public Bundle saveState(Bundle map) {
            synchronized (mSurfaceHolder) {
                if (map != null) {
                    map.putInt(KEY_DIFFICULTY, Integer.valueOf(mDifficulty));
                    map.putDouble(KEY_X, Double.valueOf(mX));
                    map.putDouble(KEY_Y, Double.valueOf(mY));
                    map.putDouble(KEY_DX, Double.valueOf(mDX));
                    map.putDouble(KEY_DY, Double.valueOf(mDY));
                    map.putDouble(KEY_HEADING, Double.valueOf(mHeading));
                }
            }
            return map;
        }

        /**
         * Sets the current difficulty.
         * 
         * @param difficulty
         */
        public void setDifficulty(int difficulty) {
            synchronized (mSurfaceHolder) {
                mDifficulty = difficulty;
            }
        }

        /**
         * Used to signal the thread whether it should be running or not.
         * Passing true allows the thread to run; passing false will shut it
         * down if it's already running. Calling start() after this was most
         * recently called with false will result in an immediate shutdown.
         * 
         * @param b true to run, false to shut down
         */
        public void setRunning(boolean b) {
            mRun = b;
        }

        /**
         * Sets the game mode. That is, whether we are running, paused, in the
         * failure state, in the victory state, etc.
         * 
         * @see #setState(int, CharSequence)
         * @param mode one of the STATE_* constants
         */
        public void setState(int mode) {
            synchronized (mSurfaceHolder) {
                setState(mode, null);
            }
        }

        /**
         * Sets the game mode. That is, whether we are running, paused, in the
         * failure state, in the victory state, etc.
         * 
         * @param mode one of the STATE_* constants
         * @param message string to add to screen or null
         */
        public void setState(int mode, CharSequence message) {
            /*
             * This method optionally can cause a text message to be displayed
             * to the user when the mode changes. Since the View that actually
             * renders that text is part of the main View hierarchy and not
             * owned by this thread, we can't touch the state of that View.
             * Instead we use a Message + Handler to relay commands to the main
             * thread, which updates the user-text View.
             */
            synchronized (mSurfaceHolder) {
                mMode = mode;

                if (mMode == STATE_RUNNING) {
                    Message msg = mHandler.obtainMessage();
                    Bundle b = new Bundle();
                    b.putString("text", "");
                    b.putInt("viz", View.INVISIBLE);
                    msg.setData(b);
                    mHandler.sendMessage(msg);
                } else {
                    mRotating = 0;
                    Resources res = mContext.getResources();
                    CharSequence str = "";
                    if (mMode == STATE_READY)
                        str = res.getText(R.string.mode_ready);
                    else if (mMode == STATE_PAUSE)
                        str = res.getText(R.string.mode_pause);
                    else if (mMode == STATE_LOSE)
                        str = res.getText(R.string.mode_lose);
                    else if (mMode == STATE_WIN)
                        str = res.getString(R.string.mode_win_prefix)
                                + mWinsInARow + " "
                                + res.getString(R.string.mode_win_suffix);

                    if (message != null) {
                        str = message + "\n" + str;
                    }

                    if (mMode == STATE_LOSE) mWinsInARow = 0;

                    Message msg = mHandler.obtainMessage();
                    Bundle b = new Bundle();
                    b.putString("text", str.toString());
                    b.putInt("viz", View.VISIBLE);
                    msg.setData(b);
                    mHandler.sendMessage(msg);
                }
            }
        }

        /* Callback invoked when the surface dimensions change. */
        public void setSurfaceSize(int width, int height) {
            // synchronized to make sure these all change atomically
            synchronized (mSurfaceHolder) {
                mCanvasWidth = width;
                mCanvasHeight = height;

                // don't forget to resize the background image
                mBackgroundImage = mBackgroundImage.createScaledBitmap(
                        mBackgroundImage, width, height, true);
            }
        }
        
        /**
         * Resumes from a pause.
         */
        public void unpause() {
            // Move the real time clock up to now
            synchronized (mSurfaceHolder) {
                mLastTime = System.currentTimeMillis() + 100;
            }

            setState(STATE_RUNNING);
        }

        private float accelxValue;
        private float accelyValue, old_accelyValue;
        private float accelzValue, old_accelzValue;
        
        private float orientxValue;
        private float orientyValue = -90;
        private float orientzValue;
        
        @Override
        public void onSensorChanged(SensorEvent event) {
            if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            	accelxValue = event.values[0];
            	accelyValue = event.values[1];
            	accelzValue = event.values[2];
            } else if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {
            	orientxValue = event.values[0];
            	orientyValue = event.values[1];
            	orientzValue = event.values[2];            
            } else {
            	return;
            }
               
            if ((accelyValue != old_accelyValue) || (accelzValue != old_accelzValue)) {
	            if (accelyValue > accelzValue) {
	            	if (accelxValue > 0) {
	            		mCar.angleDirect(360 - (90 + orientyValue));
	            	} else {
	            		mCar.angleDirect(90 + orientyValue);
	            	}
	            }
            }
            
            old_accelyValue = accelyValue;
            old_accelzValue = accelzValue;
            
            float value;
            
            value = (float) Math.sqrt((event.values[0] * event.values[0]) + 
            						   (event.values[1] * event.values[1]));

//            if (mSensorX > value) {
//            	//Log.i(this.getClass().getName(), "clockwise");
//            	mCar.turn(Car.TURN_RIGHT);
//            	
//            } else {
//            	//Log.i(this.getClass().getName(), "anticlockwise");
//            	mCar.turn(Car.TURN_LEFT);
//            }
            

            mSensorX = value;
            
            mSensorTimeStamp = event.timestamp;
            mCpuTimeStamp = System.nanoTime();
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
        }
        
        /**
         * Handles a key-down event.
         * 
         * @param keyCode the key that was pressed
         * @param msg the original event object
         * @return true
         */
        boolean doKeyDown(int keyCode, KeyEvent msg) {
            synchronized (mSurfaceHolder) {
                boolean okStart = false;
                if (keyCode == KeyEvent.KEYCODE_DPAD_UP) okStart = true;
                if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) okStart = true;
                if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) okStart = true;
                if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) okStart = true;

                if (okStart
                        && (mMode == STATE_READY || mMode == STATE_LOSE || mMode == STATE_WIN)) {
                    // ready-to-start -> start
                    doStart();
                    return true;
                } else if (mMode == STATE_PAUSE && okStart) {
                    // paused -> running
                    unpause();
                    return true;
                } else if (mMode == STATE_RUNNING) {
                    // center/space -> fire
                    if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
                    	mCar.accelerate(Car.ACCELERATE);
                        return true;
                        // left/q -> left
                    } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
                            || keyCode == KeyEvent.KEYCODE_Q) {
                    	mCar.turn(Car.TURN_LEFT);
                        return true;
                        // right/w -> right
                    } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
                            || keyCode == KeyEvent.KEYCODE_W) {
                    	mCar.turn(Car.TURN_RIGHT);
                        return true;
                        // up -> pause
                    } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
                    	mCar.accelerate(Car.BREAK);
                        return true;
                    }                
                }

                return false;
            }
        }

       
        /**
         * Handles a key-up event.
         * 
         * @param keyCode the key that was pressed
         * @param msg the original event object
         * @return true if the key was handled and consumed, or else false
         */
        boolean doKeyUp(int keyCode, KeyEvent msg) {
            boolean handled = false;

            synchronized (mSurfaceHolder) {
                if (mMode == STATE_RUNNING) {
                    if (keyCode == KeyEvent.KEYCODE_DPAD_UP
                            || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
                    	mCar.accelerate(Car.NONE);
                        handled = true;

                    } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
                            || keyCode == KeyEvent.KEYCODE_Q
                            || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
                            || keyCode == KeyEvent.KEYCODE_W) {
                        mCar.turn(Car.NONE);
                        handled = true;
                    }
                }
            }

            return handled;
        }

        private int dist = 0;

        private void UpdateRoadDivider() {
        	
            // Draw the road divider lines
            int partHeight = 20;
            
            int trackStripLeft  = (mTrackWidth / 2) - 2; //157;
            int trackStripRight = (mTrackWidth / 2) + 1; //160;
            
            //dist = trackRun;
            
            for (int i = 0; i < mNumberOfMarkers; i++) {
            	mRoadDivider.get(i).left 	= trackStripLeft;
            	mRoadDivider.get(i).right 	= trackStripRight;
            	mRoadDivider.get(i).top 	= 2 * i * partHeight + dist;
            	mRoadDivider.get(i).bottom 	= (2 * i + 1) * partHeight + dist;
            }

            if (dist <= partHeight) {
            	dist += trackRunn;
            } else if (trackRunn >= 0) {
            	dist = -partHeight;
            } else if (trackRunn < 0) {
            	dist = partHeight;
            }
        }
        
        private void drawRoadDivider(Canvas canvas) {
           
            Paint mWhitePaint;
          
            mWhitePaint = new Paint();
            mWhitePaint.setAntiAlias(true);
            mWhitePaint.setARGB(255, 255, 255, 255);
            
            for (int i = 0; i < mNumberOfMarkers; i++) {
            	
            	canvas.drawRect(mRoadDivider.get(i), mWhitePaint);
            }
        }
        
        /**
         * Draws the ship, fuel/speed bars, and background to the provided
         * Canvas.
         */
        private void doDraw(Canvas canvas) {
            // Draw the background image. Operations on the Canvas accumulate
            // so this is like clearing the screen.
            canvas.drawBitmap(mBackgroundImage, 0, 0, null);

            drawRoadDivider(canvas);

            mCar.draw(canvas);
        }

        
        /**
         * Figures the lander state (x, y, fuel, ...) based on the passage of
         * realtime. Does not invalidate(). Called at the start of draw().
         * Detects the end-of-game and sets the UI to the next state.
         */
        private void updatePhysics() {
            long now = System.currentTimeMillis();

            // Do nothing if mLastTime is in the future.
            // This allows the game-start to delay the start of the physics
            // by 100ms or whatever.
            if (mLastTime > now) return;

            double elapsed = (now - mLastTime) / 1000.0;
            
            UpdateRoadDivider();
            
            mCar.update(elapsed);

            // mRotating -- update heading
            if (mRotating != 0) {
                mHeading += mRotating * (PHYS_SLEW_SEC * elapsed);

                // Bring things back into the range 0..360
                if (mHeading < 0)
                    mHeading += 360;
                else if (mHeading >= 360) mHeading -= 360;
            }
            
            mLastTime = now;
        }
    }

    /** Handle to the application context, used to e.g. fetch Drawables. */
    private Context mContext;

    /** Pointer to the text view to display "Paused.." etc. */
    private TextView mStatusText;

    /** The thread that actually draws the animation */
    private SyotThread thread;

    public SyotView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // register our interest in hearing about changes to our surface
        SurfaceHolder holder = getHolder();
        holder.addCallback(this);

        // create thread only; it's started in surfaceCreated()
        thread = new SyotThread(holder, context, new Handler() {
            @Override
            public void handleMessage(Message m) {
                mStatusText.setVisibility(m.getData().getInt("viz"));
                mStatusText.setText(m.getData().getString("text"));
            }
        });

        setFocusable(true); // make sure we get key events
    }

	public boolean onTouchEvent(MotionEvent event) {
		super.onTouchEvent(event);
		//Log.w(this.getClass().getName(), "down");
        final int action = event.getAction();
        boolean okStart = false;
        
        switch (action) {
	        case MotionEvent.ACTION_DOWN:
	        	//Log.i(this.getClass().getName(), "down");
	        	break;
	        case MotionEvent.ACTION_UP:
	        	//Log.i(this.getClass().getName(), "up");
	        	okStart = true;
                if (okStart
                        && (thread.mMode == thread.STATE_READY || thread.mMode == thread.STATE_LOSE || thread.mMode == thread.STATE_WIN)) {
                    // ready-to-start -> start
                	thread.doStart();
                	thread.mp.start();
                	//Log.i(this.getClass().getName(), "doStart");
                    return true;
                } else if (thread.mMode == thread.STATE_PAUSE && okStart) {
                    // paused -> running
                	thread.unpause();
                	thread.mp.start();
                	//Log.i(this.getClass().getName(), "unpause");
                    return true;
                }
	        	break;
	        default:

	        	break;
        }
		return true;
	}
	
	public void onCompletion(MediaPlayer mp) {
		mp.start();
	}
	
    /**
     * Fetches the animation thread corresponding to this SyotView.
     * 
     * @return the animation thread
     */
    public SyotThread getThread() {
        return thread;
    }

    /**
     * Standard override to get key-press events.
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent msg) {
        return thread.doKeyDown(keyCode, msg);
    }

    /**
     * Standard override for key-up. We actually care about these, so we can
     * turn off the engine or stop rotating.
     */
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent msg) {
        return thread.doKeyUp(keyCode, msg);
    }

    /**
     * Standard window-focus override. Notice focus lost so we can pause on
     * focus lost. e.g. user switches to take a call.
     */
    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        if (!hasWindowFocus) thread.pause();
    }

    /**
     * Installs a pointer to the text view used for messages.
     */
    public void setTextView(TextView textView) {
        mStatusText = textView;
    }

    /* Callback invoked when the surface dimensions change. */
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        thread.setSurfaceSize(width, height);
    }

    /*
     * Callback invoked when the Surface has been created and is ready to be
     * used.
     */
    public void surfaceCreated(SurfaceHolder holder) {
        // start the thread here so that we don't busy-wait in run()
        // waiting for the surface to be created
        thread.setRunning(true);
        thread.start();
    }

    /*
     * Callback invoked when the Surface has been destroyed and must no longer
     * be touched. WARNING: after this method returns, the Surface/Canvas must
     * never be touched again!
     */
    public void surfaceDestroyed(SurfaceHolder holder) {
        // we have to tell thread to shut down & wait for it to finish, or else
        // it might touch the Surface after we return and explode
        boolean retry = true;
        thread.setRunning(false);
        while (retry) {
            try {
                thread.join();
                retry = false;
            } catch (InterruptedException e) {
            }
        }
    }
}
